<?php
namespace App\Admin\Extensions\Field;

use Encore\Admin\Form\Field;
use App\Func\Helper;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Fluent;
class Tree extends Field
{
    /**
     * 添加Form扩展组件后需要运行命令:
     * composer dump-autoload
     * @var string
     */
    protected $view = 'extensions.field.tree';

    protected static $css = [
        '/assets/jstree/themes/proton/style.min.css',
    ];

    protected static $js = [
        '/assets/jstree/jstree.min.js',
    ];

    protected $options = [
        'plugins' => ['checkbox', 'types'],
        'core'    => [
            'check_callback' => true,
            'themes' => [
                'name'       => 'proton',
                'responsive' => true,
            ],
        ],
        'checkbox' => [
            'keep_selected_style' => false,
        ],
        'types' => [
            'default'  => [
                'icon' => false,
            ],
        ],
    ];

    /**
     * @var array
     */
    protected $nodes = [];

    /**
     * @var array
     */
    protected $parents = [];

    /**
     * @var array
     */
    protected $columnNames = [
        'id'     => 'id',
        'text'   => 'name',
        'parent' => 'parent_id',
    ];

    /**
     * @var bool
     */
    protected $filterParents = true;

    /**
     * @return $this
     */
    public function disableFilterParents()
    {
        $this->filterParents = false;

        return $this;
    }

    public function setIdColumn(string $name)
    {
        $this->columnNames['id'] = $name;

        return $this;
    }

    public function setTitleColumn(string $name)
    {
        $this->columnNames['text'] = $name;

        return $this;
    }

    public function setParentColumn(string $name)
    {
        $this->columnNames['parent'] = $name;

        return $this;
    }

    protected function values()
    {
        return $this->form ? $this->form->model() : new Fluent();
    }

    protected function formatNodes()
    {
        $value = Helper::array(
            old($this->column, $this->value())
        );

        $this->value = &$value;

        if ($this->nodes instanceof \Closure) {
            $this->nodes = $this->nodes->call($this->values(), $this->value(), $this);
        }

        if (! $this->nodes) {
            return;
        }

        $idColumn = $this->columnNames['id'];
        $textColumn = $this->columnNames['text'];
        $parentColumn = $this->columnNames['parent'];

        $parentIds = $nodes = [];

        foreach ($this->nodes as &$v) {
            if (empty($v[$idColumn])) {
                continue;
            }

            $parentId = $v[$parentColumn] ?? '#';
            if (empty($parentId)) {
                $parentId = '#';
            } else {
                $parentIds[] = $parentId;
            }

            $v['state'] = [];

            if ($value && in_array($v[$idColumn], $value)) {
                $v['state']['selected'] = true;
            }

            $nodes[] = [
                'id'     => $v[$idColumn],
                'text'   => $v[$textColumn] ?? null,
                'parent' => $parentId,
                'state'  => $v['state'],
            ];
        }

        if ($this->filterParents) {
            // 筛选出所有父节点，最终点击树节点时过滤掉父节点
            $this->parents = array_unique($parentIds);
        }

        $this->nodes = &$nodes;
    }

    /**
     * @param array|Arrayable|\Closure $data exp:
     * {
     *      "id": "1",
     *      "parent": "#",
     *      "text": "Dashboard",
     *      "state": {"selected": true}
     * }
     * @return $this
     */
    public function nodes($data)
    {
        if ($data instanceof Arrayable) {
            $data = $data->toArray();
        }
        $this->nodes = &$data;
        return $this;
    }

    public function render()
    {
        $this->attribute('type', 'hidden');

        $this->formatNodes();

        if ($v = $this->value()) {
            $this->attribute('value', implode(',', $v));
        }

        $this->addVariables([
            'options'    => json_encode($this->options),
            'nodes'      => json_encode($this->nodes),
            'parents'    => json_encode($this->parents),
        ]);

        return parent::render();
    }

    public function prepare($value)
    {
        $value = explode(',',$value);
        return array_filter($value, 'strlen');
    }
}
